// 智能指针
pub fn learn_smartpoint() {
  //_learn_box_0();
  // _learn_normal_point();
  //_learn_box_point();
  //_learn_mybox0();
  //_learn_mybox_hello();
  //_learn_rc_clone();
  //_learn_rc_refcell();
  _learn_rc_refcell1();
}

// Box<T>
// 允许你将一个值放在堆上而不是栈上.留在栈上的则是指向堆数据的指针.
// 常用场景
// 当有一个在编译时未知大小的类型,而又想要在需要确切大小的上下文中使用这个类型值的时候
//     Box允许创建递归类型

// 当有大量数据并希望在确保数据不被拷贝的情况下转移所有权的时候
//     转移大量数据的所有权可能会花费很长时间,因为数据在栈上进行了拷贝.为了改善这种情况下
// 的性能,可以通过box将这些数据储存在堆上.接着,只有少量的指针数据在栈上被拷贝.

// 当希望拥有一个值并只关心它的类型是否实现了特定trait而不是其具体类型的时候
//     trait对象

// 使用Box<T>在堆上存储数据
// 语法
fn _learn_box_0() {
  let a = 1;
  // 变量b的值是一个指向被分配在堆上的值5的Box
  let b = Box::new(5);
  let s = Box::new(String::from("hello"));
  println!("a={}, a={}, a={:p}", a, &a, &a);
  println!("b={}, b={}, b={}, b={:p}, b={}", b, &b, *b, b, &&&&&&&b);
  println!("s={}", s);
} // b离开作用域时将被释放.作用于box本身(位于栈上)和它所指向的数据(位于堆上)

// Box允许创建递归类型
// 递归类型(recursive type)的值可以拥有另一个同类型的值作为其自身的一部分.
// 但存在问题,因为rust在编译时要知道类型占用多少空间.递归类型的值嵌套理论上可以
// 无限的进行下去.所以rust不知道递归类型需要多少空间. 因为box有一个已知的大小,所以
// 通过在循环类型定义中插入box,就可以创建递归类型了.

// 利用cons list来展示递归类型的概念
// cons函数,利用两个参数来构造一个新的列表.通过对一个包含值的列表和另一个值调用cons,
// 可以构建由递归列表组成的cons list.
// 如下伪代码,有一个包含列表1,2,3的cons list,其每一个列表在一个括号中,
// (1,(2,(3, Nil)))
// cons list的每一项都包含两个元素:当前项的值和下一项.其最后一项包含一个叫做Nil
// 的值且没有下一项. cons list通过递归调用cons函数产生.代表递归的终止条件(base case)
// 的规范名称是Nil,它宣布列表的终止.这与"null"或"nil"不同,它们代表无效或缺失的值

// cons list并不是一个rust中常见的类型。大部分在rust中需要列表的时候,Vec<T>是一个更好
// 的选择.其他更为复杂的递归数据类型 确实 在rust的很多场景中很有用,不过通过以cons list
// 作为开始,我们可以探索如何使用box毫不费力的定义一个递归数据类型.

// 一个cons list的枚举定义,暂时还不能编译,因为这个类型没有已知的大小
/*
enum List { // error, recursive type 'List' has infinite size
    Cons(i32, List),
    Nil,
}
use crate::smartpoint::List::{Cons, Nil};

fn _learn_recursive_type(){
    let list = Cons(1, Cons(2, Cons(3, Nil)));
}
*/

//上例中,rust无法计算为了存放List值到底需要多少空间,我们需要逐步分析,先了解一下
//rust如何决定需要多少空间来存放一个非递归类型

// 计算非递归类型的大小
// 如下:rust 检查Message的每一个成员
enum Message {
  Quit,
  Move { x: i32, y: i32 },
  Write(String),
  ChangeColor(i32, i32, i32),
}

// 不同类型的enum成员需要的空间不一样,enum实际上只会使用其中一个成员,因此Message值
// 所需的空间等于存储其最大成员的空间大小

// 使用Box<T>给递归类型一个已知的大小
// 因为Box<T>是一个指针,我们总是知道它需要多少空间:指针的大小并不会根据其指向的数据量而改变.
// 这意味着可以将Box放入Cons成员中而不是直接存放另一个List值.
// Box会指向另一个位于堆上的List值,而不是存放在Cons成员中.
// 从概念上讲,我们仍然有一个通过在其中"存放"其他列表创建的列表,不过现在实现这个概念的方式更像是一个
// 项挨着另一项,而不是一项包含另一项

enum List {
  // error, recursive type 'List' has infinite size
  Cons(i32, Box<List>),
  Nil,
}
use crate::smartpoint::List::{Cons, Nil};

fn _learn_recursive_type() {
  let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
}
// Cons成员将会需要一个i32的大小加上储存box指针数据的空间. Nil成员不储存值,所以它比Cons成员需要更少的空间.
// box只提供了间接存储和堆分配;它们并没有任何其他的特殊的功能.也就没有这些特殊功能带来的性能损失.

// 通过Deref trait将智能指针当做常规引用处理
// 追踪指针的值
fn _learn_normal_point() {
  let x = 5;
  let y = &x;
  assert_eq!(5, x);
  assert_eq!(5, *y); // y是x的引用,要对y指向的值进行比较,必须用 * 来解引用.
}
// 像引用一样使用Box<T>
fn _learn_box_point() {
  let x = 5;
  let y = Box::new(x);
  assert_eq!(5, x);
  assert_eq!(5, *y); // 与上例不同的,是将y设置为一个指向x值拷贝的Box<T>实例,而不是指向x值的引用
}

// 自定义智能指针
// 探索默认情况下智能指针与引用的不同
struct MyBox<T>(T);

impl<T> MyBox<T> {
  fn new(x: T) -> MyBox<T> {
    MyBox(x)
  }
}

fn _learn_mybox0() {
  let x = 5;
  let y = MyBox::new(x);
  assert_eq!(5, x);
  // 实现Deref之前会报错,rust不知道如何解引用 MyBox
  assert_eq!(5, *y);
  // 实现Deref之后, 对于*y, rust实际运行 *(y.deref())
  // rust将 * 运算符替换为先调用deref方法再进行普通解引用的操作.这样我们就不需要再手动调用deref方法.
  // 返回值的引用,以及*(y.deref())括号外的普通解引用仍然必须(没有省略掉)的原因在于所有权.
  // 如果deref方法直接返回值而不是值的引用,其值(的所有权)将被移出self.在这里以及大部分使用解引用运算符
  // 的情况下我们并不希望获取MyBox<T>内部值的所有权.
  // 对实现Deref的类型,将一个 * 替换为 *(y.deref()) 的行为只会发生一次.
}

// 通过实现Deref trait将某个类型像引用一样处理

use std::ops::Deref;

impl<T> Deref for MyBox<T> {
  // 关联类型
  type Target = T;
  fn deref(&self) -> &Self::Target {
    &self.0
  }
}

// 函数和方法的隐式Deref强制转换
// Deref强制转换(deref coercions)将实现了Deref trait的类型的引用转换为另一种类型的引用.只作用于
// 实现了Deref trait的类型.
// 当这种特定类型的引用作为实参传递给和形参类型不同的函数或方法时将自动进行.
fn _hello(name: &str) {
  println!("Hello, {name}!");
}
fn _learn_mybox_hello() {
  let m = MyBox::new(String::from("rust"));
  // 自动隐式转换:1,&m是MyBox值的引用,因为MyBox实现了Deref,所以&m会被自动转换为&String
  // 2,&String不是需要的&str, 但是String也实现了Deref,所以&String会被自动转换为&str.
  // 3, &str与_hello需要的参数类型相符,传参成功
  _hello(&m);

  // 如果不走强制转换,就要像下面这样调用函数:
  // 1,*m, 解引用,得到MyBox中的值,即String
  // 2,(*m)[..], 得到String的slice,即str
  // 3, &(*m)[..], 得到str的引用,即&str
  // _hello(&(*m)[..]);
}

// Deref强制转换如何与可变性交互
// 使用Deref trait重载不可变引用的*运算符, rust也提供了DerefMut trait用于重载可变引用的 * 运算符
// rust在发现类型和trait实现满足三种情况时会进行Deref强制转换:
// 1, 当 T:Deref<Target=U>时,从&T到&U
// 2, 当 T:DerefMut<Target=U>时,从&mut T到&mut U
// 3, 当 T:Deref<Target=U>时,从&mut T到&U

// 对于1,2,如果有一个&T,而T实现了返回U的Deref,则可以直接得到&U. 可变不可变都适用.
// 对于3, 要考虑借用规则. 根据借用规则,如果有一个可变引用,其必须是数据的唯一引用.
// 将一个可变引用转换为不可变引用不会破坏借用规则,而将不可变引用转换为可变引用则需要该不可变引用是数据
// 唯一的引用.因此,从3看出,rust自动强制转换只支持从可变引用转换为不可变引用.
// 而不支持将不可变引用转换为可变引用

// 使用Drop trait运行清理代码
// 通过std::mem::drop提早丢弃值
// 场景:使用智能指针管理锁时,可能希望强制运行drop方法来释放锁以便作用域中的其他代码可以获取锁.
// 不允许显式调用drop是因为rust仍然会在其离开作用域时自动调用drop,这会导致 double free.

// RC<T> 引用计数智能指针
// 引用计数意味着记录一个值的引用数量来知晓这个值是否仍在被使用.如果某个值有零个引用,就代表没有任何有效
// 引用,就可以被清理.
// Rc<T> 用于当我们希望在堆上分配一些内存,供程序的多个部分读取,且无法在编译时确定程序的哪一部分会
// 最后结束使用它的时候.如果确实知道哪部分是最后一个结束使用的话,就可以令其成为数据的所有者.正常的
// 所有权规则就可以在编译时生效.

// 使用Rc<T>共享数据
fn _learn_rc_share0() {
  let a = Cons(5, Box::new(Cons(10, Box::new(Nil))));
  let b = Cons(3, Box::new(a));
  // let c = Cons(4, Box::new(a));//err, use of moved value:`a`; value used here after moved
}

enum List2 {
  Cons2(i32, Rc<List2>),
  Nil2,
}

use crate::smartpoint::List2::{Cons2, Nil2};
use std::rc::Rc;

fn _learn_rc_share1() {
  let a = Rc::new(Cons2(5, Rc::new(Cons2(10, Rc::new(Nil2)))));
  let b = Cons2(3, Rc::clone(&a));
  let c = Cons2(4, Rc::clone(&a));
}

// Rc::clone只会增加引用计数.不会损耗多少性能.
// 克隆Rc<T>会增加引用计数
fn _learn_rc_clone() {
  let a = Rc::new(Cons2(5, Rc::new(Cons2(10, Rc::new(Nil2)))));
  println!("count after creating a = {}", Rc::strong_count(&a));
  let b = Cons2(3, Rc::clone(&a));
  println!("count after creating b = {}", Rc::strong_count(&a));
  {
    let c = Cons2(4, Rc::clone(&a));
    println!("count after creating c = {}", Rc::strong_count(&a));
  }
  println!("count after c goes out of scope = {}", Rc::strong_count(&a));
}

// RefCell<T> 与内部可变性模式
// 内部可变性是rust中的一个设计模式,它允许你即使在有不可变引用时也可以改变数据.这通常是借用规则所不允许的.
// 为了改变数据,该模式在数据结构中使用unsafe代码来模糊rust通常的可变性和借用规则.unsafe代码表明我们
// 在手动检查这些规则,而不是让编译器替我们检查.
// 当可以确保代码在运行时会遵守借用规则,即使编译器不能保证的情况,可以选择使用那些运用内部可变性模式的类型.

// 通过RefCell<T>在运行时检查借用规则
// 不同于Rc<T>, RefCell<T>代表其数据的唯一的所有权.那么是什么让RefCell<T>不同于像Box<T>这样的类型呢?

// 借用规则:1,在任意时刻,只能拥有一个可变引用或任意数量的不可变引用之一(而不是两者)
// 2,引用必须总是有效的.

// 对于引用和Box<T>, 借用规则的不可变性作用于编译时.对于RefCell<T>,这些不可变性作用于运行时.
// 在编译时检查借用规则的优势是这些错误将在开发过程的早期被捕获,同时对运行时没有性能影响.因此,在编译时
// 检查借用规则是大部分情况的最佳选择.
// 在运行时检查借用规则的好处是允许出现特定内存安全的场景,而它们在编译时检查中是不允许的.
// 有一些分析是不可能完成的,rust编译器将不能通过所有权规则编译,就可能会拒绝一个正确的程序.虽然给程序员
// 带来不便,但不会带来灾难.RefCell<T>正是用于当你确信代码遵守借用规则,而编译器不能理解和确定的时候.

// 使用Box<T>,Rc<T>或RefCell<T>的理由:
// Rc<T>允许相同数据拥有多个所有者;Box<T>和RefCell<T>只能有一个所有者.
// Box<T> 允许在编译时执行不可变或可变借用检查; Rc<T>仅允许在编译时执行不可变借用检查;RefCell<T>允许
// 在运行时执行不可变或可变借用检查.
// 因为RefCell<T> 允许在运行时执行可变借用检查,所以我们可以在即便RefCell<T>自身是不可变的情况下修改
// 其内部的值.

// 在不可变值内部改变值就是 内部可变性模式.

// 内部可变性:不可变值的可变借用
// 借用规则的一个推论是当有一个不可变值时,不能可变地借用它.
fn _learn_borrow_mut() {
  let x = 5;
  // let y = &mut x; // err:can't mutate immutable variable 'x'
}

// 内部可变性的用例: mock对象
pub trait Messenger {
  fn send(&self, msg: &str);
}
// 结构体的作用域<=messenger这个引用的作用域
pub struct LimitTracker<'a, T: Messenger> {
  messenger: &'a T,
  value: usize,
  max: usize,
}
impl<'a, T> LimitTracker<'a, T>
where
  T: Messenger,
{
  // messenger引用的生命周期>=返回值的生命周期
  pub fn new(messenger: &'a T, max: usize) -> LimitTracker<'a, T> {
    LimitTracker {
      messenger,
      value: 0,
      max,
    }
  }
  pub fn set_value(&mut self, value: usize) {
    self.value = value;
    let percentage_of_max = self.value as f64 / self.max as f64;
    if percentage_of_max >= 1.0 {
      self.messenger.send("Error: You are over your quota!");
    } else if percentage_of_max >= 0.9 {
      self
        .messenger
        .send("Urgent warning: You've used up over 90% of your quota!");
    } else if percentage_of_max >= 0.75 {
      self
        .messenger
        .send("Warning: You've used up over 75% of your quota!");
    }
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use std::cell::RefCell;
  struct MockMessenger {
    // sent_messages: Vec<String>,
    sent_messages: RefCell<Vec<String>>,
  }
  impl MockMessenger {
    fn new() -> MockMessenger {
      MockMessenger {
        // sent_messages: vec![],
        sent_messages: RefCell::new(vec![]),
      }
    }
  }
  impl Messenger for MockMessenger {
    // &self 是不可变引用,也不能改为可变引用,这样就与messenger的定义不兼容了.
    // 这里就是内部可变性的使用场景,通过RefCell来存储sent_messages
    fn send(&self, message: &str) {
      // self.sent_messages.push(String::from(message));
      // 调用RefCell的borrow_mut方法来获取RefCell中的值的可变引用.
      self.sent_messages.borrow_mut().push(String::from(message))
    }
  }
  #[test]
  fn it_sends_an_over_75_percent_warning_message() {
    let mock_messenger = MockMessenger::new();
    let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);
    limit_tracker.set_value(80);
    //assert_eq!(mock_messenger.sent_messages.len(), 1)
    // 调用RefCell的borrow方法来获取RefCell中的值的不可变引用.
    assert_eq!(mock_messenger.sent_messages.borrow().len(), 1);
  }

  // RefCell<T> 在运行时记录借用
  // 当创建不可变和可变引用时,我们分别使用&和&mut语法.对于RefCell<T>来说,则是borrow和borrow_mut方法,这属于
  // RefCell<T>安全API的一部分.borrow方法返回Ref<T>类型的智能指针,borrow_mut方法返回RefMut<T>类型的智能指针.
  // 这两个类型都实现了Deref,所以可以当做常规引用对待.

  // RefCell<T>记录当前有多少个活动的Ref<T>和RefMut<T>智能指针.每次调用borrow,RefCell<T>将活动的不可变借用
  // 计数加一.当Ref<T>值离开作用域时,不可变借用计数减一.就像编译时借用规则一样,RefCell<T>在任何时候只允许
  // 有多个不可变借用或一个可变借用.

  // 演示RefCell<T>的运行时错误
  struct MockMessenger2 {
    // sent_messages: Vec<String>,
    sent_messages: RefCell<Vec<String>>,
  }
  impl MockMessenger2 {
    fn new() -> MockMessenger2 {
      MockMessenger2 {
        // sent_messages: vec![],
        sent_messages: RefCell::new(vec![]),
      }
    }
  }
  // 在同一个作用域中创建两个可变引用并观察RefCell<T>的panic
  impl Messenger for MockMessenger2 {
    fn send(&self, message: &str) {
      let mut one_borrow = self.sent_messages.borrow_mut();
      // 下行代码运行时报错:panic: already borrowed: BorrowMutError
      let mut two_borrow = self.sent_messages.borrow_mut();
      one_borrow.push(String::from(message));
      two_borrow.push(String::from(message));
    }
  }

  #[test]
  fn _watch_panic() {
    let mock_messenger = MockMessenger2::new();
    mock_messenger.send("hello");
  }
}

// 结合Rc<T>和RefCell来拥有多个可变数据所有者
use std::cell::RefCell;
#[derive(Debug)]
enum List3 {
  Cons3(Rc<RefCell<i32>>, Rc<List3>),
  Nil3,
}

use crate::smartpoint::List3::{Cons3, Nil3};

// 使用Rc<RefCell<i32>>创建可修改的List
fn _learn_rc_refcell() {
  let value = Rc::new(RefCell::new(5));
  // 使用Rc::clone以便a和value都能拥有其内部值5的所有权,而不是将所有权从value移动到a,或者让a借用value
  let a = Rc::new(Cons3(Rc::clone(&value), Rc::new(Nil3)));
  let b = Cons3(Rc::new(RefCell::new(3)), Rc::clone(&a));
  let c = Cons3(Rc::new(RefCell::new(4)), Rc::clone(&a));
  *value.borrow_mut() += 10;
  println!("a after={:?}", a);
  println!("b after={:?}", b);
  println!("c after={:?}", c);
}

// 引用循环会导致内存泄漏
// 制造引用循环
#[derive(Debug)]
enum List4 {
  // 这个定义可以修改Cons4成员锁指向的List
  Cons4(i32, RefCell<Rc<List4>>),
  Nil4,
}

use crate::smartpoint::List4::{Cons4, Nil4};

impl List4 {
  fn tail(&self) -> Option<&RefCell<Rc<List4>>> {
    match self {
      Cons4(_, item) => Some(item),
      Nil4 => None,
    }
  }
}

// 创建一个引用循环,两个List值互相指向彼此
fn _learn_rc_refcell1() {
  let a = Rc::new(Cons4(5, RefCell::new(Rc::new(Nil4))));
  println!("a initial rc count={}", Rc::strong_count(&a));
  println!("a next item={:?}", a.tail());

  let b = Rc::new(Cons4(10, RefCell::new(Rc::clone(&a))));

  println!("a rc count after b creation={}", Rc::strong_count(&a));
  println!("b initial rc count={}", Rc::strong_count(&b));
  println!("b next item={:?}", b.tail());

  if let Some(link) = a.tail() {
    *link.borrow_mut() = Rc::clone(&b);
  }

  println!("b rc count after chaninging a={}", Rc::strong_count(&b));
  println!("a rc count after chaninging a={}", Rc::strong_count(&a));

  // 取消下一行的注释,这里会有一个循环,将会溢出栈
  // println!("a next item={:?}", a.tail());
}
